#!/usr/bin/ruby

# Copyright (C) 2016 Open Source Robotics Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# We use 'dl' for Ruby <= 1.9.x and 'fiddle' for Ruby >= 2.0.x
if RUBY_VERSION.split('.')[0] < '2'
  require 'dl'
  require 'dl/import'
  include DL
else
  require 'fiddle'
  require 'fiddle/import'
  include Fiddle
end

require 'optparse'

# Constants.
LIBRARY_NAME = '@library_location@'
LIBRARY_VERSION = '@PROJECT_VERSION_FULL@'
COMMON_OPTIONS =
               "  -h [ --help ]              Print this help message.\n"\
               "  --force-version <VERSION>  Use a specific library version.\n"\
               '  --versions                 Show the available versions.'
COMMANDS = {  'msg' =>
                  "Print information about ignition messages.\n\n" +
                  "  ign msg [options]\n\n" +
                  "Options:\n\n" +
                  "  -i [ --info ] arg          " +
                  "Get info about the specified message type.\n" +
                  "  -l [ --list ]              List all message types.\n" +
                  COMMON_OPTIONS
}

#
# Class for the Ignition msgs command line tools.
#
class Cmd
  #
  # Return a structure describing the options.
  #
  def parse(args)
    options = {}

    usage = COMMANDS[args[0]]

    # Read the command line arguments.
    opt_parser = OptionParser.new do |opts|
      opts.banner = usage

      opts.on('-h', '--help', 'Print this help message') do
        puts usage
        exit(0)
      end

      opts.on('-l', '--list', 'Print all message types') do |l|
        options['list'] = l
      end

      opts.on('-i msg', '--info', String,
              'Print information about a message') do |t|
        options['info'] = t
      end
    end
    begin
      opt_parser.parse!(args)
    rescue
      puts usage
      exit(-1)
    end

    # Check that there is at least one command and there is a plugin that knows
    # how to handle it.
    if ARGV.empty? || !COMMANDS.key?(ARGV[0]) ||
       options.empty?
      puts usage
      exit(-1)
    end

    options['command'] = ARGV[0]

    options
  end  # parse()

  def execute(args)
    options = parse(args)

    # puts 'Parsed:'
    # puts options

    # Read the plugin that handles the command.
    if LIBRARY_NAME[0] == '/'
      # If the first character is a slash, we'll assume that we've been given an
      # absolute path to the library. This is only used during test mode.
      plugin = LIBRARY_NAME
    else
      # We're assuming that the library path is relative to the current
      # location of this script.
      plugin = File.expand_path(File.join(File.dirname(__FILE__), LIBRARY_NAME))
    end
    conf_version = LIBRARY_VERSION

    begin
      Importer.dlload plugin
    rescue DLError
      puts "Library error: [#{plugin}] not found."
      exit(-1)
    end

    # Read the library version.
    Importer.extern 'const char* ignitionMsgsVersion()'
    begin
      plugin_version = Importer.ignitionMsgsVersion.to_s
    rescue DLError
      puts "Library error: Problem running 'ignitionMsgsVersion()' from #{plugin}."
      exit(-1)
    end

    # Sanity check: Verify that the version of the yaml file matches the version
    # of the library that we are using.
    unless plugin_version.eql? conf_version
      puts "Error: Version mismatch. Your configuration file version is
            [#{conf_version}] but #{plugin} version is [#{plugin_version}]."
      exit(-1)
    end

    begin
      case options['command']
      when 'msg'
        if options.key?('list')
          Importer.extern 'void cmdMsgList()'
          Importer.cmdMsgList
        elsif options.key?('info')
          Importer.extern 'void cmdMsgInfo(const char *)'
          Importer.cmdMsgInfo(options['info'])
        else
          puts 'Command error: I do not have an implementation '\
               'for this command.'
        end
      else
        puts 'Command error: I do not have an implementation for '\
             "command [ign #{options['command']}]."
      end
    rescue
      puts "Library error: Problem running [#{options['command']}]() "\
           "from #{plugin}."
    end
  end
end
